using Engine.AddinManager;
using Engine.Graphics;
using ForestBorder.AddinManager;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

namespace ForestBorder
{
    internal struct Properties
    {
        #region Private Fields

        private const byte versionsNumber = 0x03; // 03.02.2017
        private Dictionary<ICreateForestBorderMesh, DataForForestvertexCalclation> gameVertexCreationData;
        private float heightEnd;
        private float heightOffset;
        private float heightStart;
        private IMaterial material;
        private float texturCoordinateScale;
        private float textureOffsetStarts;

        #endregion Private Fields

        #region Public Constructors

        public Properties(Properties properties)
        {
            this.heightEnd = properties.heightEnd;
            this.heightOffset = properties.heightOffset;
            this.heightStart = properties.heightStart;
            this.material = properties.material;
            this.texturCoordinateScale = properties.texturCoordinateScale;
            this.textureOffsetStarts = properties.textureOffsetStarts;

            this.gameVertexCreationData = new Dictionary<ICreateForestBorderMesh, DataForForestvertexCalclation>();
            foreach (var addin in properties.gameVertexCreationData.Keys)
            {
                this.gameVertexCreationData.Add(addin, properties.gameVertexCreationData[addin]);
            }
        }

        #endregion Public Constructors

        #region Internal Constructors

        internal Properties(float heightStart, float heightEnd, IMaterial material)
        {
            // Version 0
            this.heightStart = heightStart;
            this.heightEnd = heightEnd;
            this.material = material;
            this.textureOffsetStarts = 0.0f;
            this.heightOffset = 0.0f;
            this.texturCoordinateScale = 2.0f;
            gameVertexCreationData = new Dictionary<ICreateForestBorderMesh, DataForForestvertexCalclation>();
        }

        internal Properties(float heightStart, float heightEnd, float textureOffset, IMaterial material)
            : this(heightStart, heightEnd, material)
        {
            // Version 1
            this.textureOffsetStarts = textureOffset;
        }

        internal Properties(float heightStart, float heightEnd, float textureOffset, IMaterial material, float offsetY, float textureScale)
            : this(heightStart, heightEnd, textureOffset, material)
        {
            // Version 2
            this.texturCoordinateScale = textureScale;
            this.heightOffset = offsetY;
        }

        #endregion Internal Constructors

        #region Public Properties

        public float HeightEnd
        {
            get { return heightEnd; }
            set { heightEnd = value; }
        }

        public float HeightOffset
        {
            get { return heightOffset; }
            set { heightOffset = value; }
        }

        public float HeightStart
        {
            get { return heightStart; }
            set { heightStart = value; }
        }

        public IMaterial Material
        {
            get { return material; }
            set { material = value; }
        }

        public float TexturCoordinateScale
        {
            get { return texturCoordinateScale; }
            set { texturCoordinateScale = value; }
        }

        public float TextureOffsetStarts
        {
            get { return textureOffsetStarts; }
            set { textureOffsetStarts = value; }
        }

        #endregion Public Properties

        #region Internal Methods

        internal static Properties FromStream(BinaryReader br, IAddin addin, MaterialManager materialManager, AddinManager.AddinManagerForestBorderAddin addinmanager)
        {
            byte version = br.ReadByte();

            switch (version)
            {
                case 0x01:// Version 1
                    return new Properties(br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), addin.LoadMaterial(br, materialManager));

                case 0x02:// Version 2
                    return new Properties(br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), addin.LoadMaterial(br, materialManager), br.ReadSingle(), br.ReadSingle());

                case 0x03:// Version 3
                    return LoadV3(br, addin, addinmanager, materialManager);
            }

            Debug.WriteLine("Error while loading properties from ForestBorder Addin!");

            throw new Exception("aktuelle material from material control");
            // return new Properties(100, 100, addin.LoadMaterialXML("defaultM"), EnumMappingMode.material32));
        }

        internal DataForForestvertexCalclation GetGameProperties(ICreateForestBorderMesh addin)
        {
            DataForForestvertexCalclation data;

            if (this.gameVertexCreationData.TryGetValue(addin, out data))
                return data;

            return addin.GetStandardVertexData();
            // throw new Exception("No Data for addin: " + addin.GetGameName());
        }

        internal void Save(System.IO.BinaryWriter writer, AddinManager.AddinManagerForestBorderAddin addinmanager)
        {
            try
            {
                writer.Write(Properties.versionsNumber);
                writer.Write(this.heightStart);
                writer.Write(this.heightEnd);
                writer.Write(this.TextureOffsetStarts);
                this.material.SaveToStream(writer);
                writer.Write(this.HeightOffset);
                writer.Write(this.TexturCoordinateScale);

                if (gameVertexCreationData == null)
                    gameVertexCreationData = new Dictionary<ICreateForestBorderMesh, DataForForestvertexCalclation>();

                // Write number of gameaddins.
                writer.Write((int)this.gameVertexCreationData.Keys.Count);

                foreach (ICreateForestBorderMesh addin in this.gameVertexCreationData.Keys)
                {
                    // Crate a temporary binarywriter to get the data for road created by addin.
                    using (MemoryStream ms = new MemoryStream())
                    using (BinaryWriter bwData = new BinaryWriter(ms))
                    {
                        DataForForestvertexCalclation data;

                        if (this.gameVertexCreationData.TryGetValue(addin, out data))
                        {
                            // Save data to stream
                            addin.SaveForestData(data, bwData);
                            // Set the datasize.
                            data.sizeInBytes = (int)bwData.BaseStream.Length;
                        }
                        else
                        {
                            // Save empty Properties
                            data = new DataForForestvertexCalclation()
                            {
                                sizeInBytes = 0,
                                version = 0
                            };
                        }

                        // Write data to file.
                        writer.Write(addin.GetGameName());
                        writer.Write((int)data.sizeInBytes);
                        writer.Write((int)data.version);
                        // Write gamedll data which was stored in memory stream.
                        using (BinaryReader br = new BinaryReader(ms))
                        {
                            ms.Position = 0;
                            writer.Write(br.ReadBytes((int)br.BaseStream.Length));
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        internal void SetGameVertexData(ICreateForestBorderMesh addinScenarioEditor, DataForForestvertexCalclation dataForGamevertexCalclation)
        {
            if (this.gameVertexCreationData == null)
                this.gameVertexCreationData = new Dictionary<ICreateForestBorderMesh, DataForForestvertexCalclation>();

            if (this.gameVertexCreationData.ContainsKey(addinScenarioEditor))
                this.gameVertexCreationData[addinScenarioEditor] = dataForGamevertexCalclation;
            else
                this.gameVertexCreationData.Add(addinScenarioEditor, dataForGamevertexCalclation);
        }

        #endregion Internal Methods

        #region Private Methods

        private static Properties LoadV3(BinaryReader br, IAddin addin, AddinManagerForestBorderAddin addinmanager, MaterialManager materialManager)
        {
            Properties properties = new Properties();
            properties.HeightStart = br.ReadSingle();
            properties.HeightEnd = br.ReadSingle();
            properties.TextureOffsetStarts = br.ReadSingle();
            properties.Material = addin.LoadMaterial(br, materialManager);
            properties.HeightOffset = br.ReadSingle();
            properties.TexturCoordinateScale = br.ReadSingle();

            int numberOfVertexDataAddins = br.ReadInt32();

            properties.gameVertexCreationData = new Dictionary<ICreateForestBorderMesh, DataForForestvertexCalclation>();
            for (int i = 0; i < numberOfVertexDataAddins; i++)
            {
                string addinName = br.ReadString();
                int sizeInBytes = br.ReadInt32();
                int version = br.ReadInt32();

                long currentPositionInStream = br.BaseStream.Position;

                var streetAddin = addinmanager.GetAddin(addinName);

                properties.SetGameVertexData(streetAddin, streetAddin.ReadForestAddinData(br, version));

                br.BaseStream.Position = currentPositionInStream + sizeInBytes;
            }

            return properties;
        }

        #endregion Private Methods
    }
}